﻿#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include <sys/types.h>
#include <fcntl.h>

#include <errno.h>
#include <math.h>
#include <fcntl.h>


#include <list>
#include <iostream>
using namespace std;

#include "time.h"
#include "string.h"
#include "cv.h"
#include "cvcam.h"

//#include "socket.h"
#include "./Socket/socket.h"
#include "./DIB/dib.h"
#include "./tools/CPtrList.h"
#include "./ATMFace/atmfaceselect.h"

//#include "detect.h"
//add detect shadow

#include "./ATMObj/ObjDetect.h"
#include "./ATMObj/ObjStableDetect.h"
#include "./Packets/package.h"
#include "./Alarm/alarm.h"
#include "./Public/public.h"
//#include "./SystemStatus/systemstatus.h"
#include "./Configure/configure.h"
#include "RectMatch/RectMatch.h"

//#include "./Socket/jssocket.h"
//#include "./Socket/jsiomodel.h"
#include "./Packets/jsmsg.h"
#include "VideoCapture/videocapture.h"
#include "../Cameras/Providers/WebCam.h"
#include "../Cameras/Providers/Sdk2000.h"
#include "../Cameras/Providers/VideoFileCam.h"
#include "../Cameras/Providers/DirectoryCam.h"
#include "../lib/FaceSearch/FaceSelect.h"
#include "CameraBlockedDetect.h"


//预处理图??
extern bool PreProcessFrame(IplImage *img);
extern void WriteFile(const char* filename, char* data, int len);
extern IplImage * cvCvtDIB2IMG(CDIB * pDib);
extern CDIB * cvCvtIMG2DIB(IplImage * pImg);

extern int RecvMsgFrom(int nFd, CJSMsg &msg, struct sockaddr_in *from);
extern int SendMsgTo(int nFd, CJSMsg &msg, struct sockaddr_in *to);
extern int JS_UDPServer(int nPort);

struct ProImage
{
 
    ProImage()
    {
        ZeroMemory(this, sizeof(ProImage));
    }

    time_t time;
    IplImage *pIplImage;
};


int g_ndayH;
int g_ndayL;
int g_chTimeH;
int g_chTimeL;

//CVideo g_videos[2];
//CJSPtrList g_ProcessBuf[3];
CATMPtrList g_ProcessBuf[3];

CSocket *pResultSock;
CAlarm *g_pAlarm;               //一个CAlarm类可以处理多个ATM机的报警
//CSystemStatus *g_pSystemStatus; //一个CSystemStatus对应一个ATM机状态
CConfigure *g_pConfigure;

int g_nCaptureThreadOK[2];
int b_videoStatus[3]; //视频状??
bool g_bQuit = false;



//Hprocess与Vprocess共同维护此布尔变??
volatile bool bPerson = false;


IplImage* GetSubImage(IplImage* pOriImage, CvRect roi)
{
    IplImage * pSubImage = NULL;

    cvSetImageROI(pOriImage, roi);
    pSubImage = cvCreateImage(cvSize(pOriImage->roi->width,
            pOriImage->roi->height), pOriImage->depth, pOriImage->nChannels);
    pSubImage->origin = pOriImage->origin;
    cvCopy(pOriImage, pSubImage);
    cvResetImageROI(pOriImage);

    return pSubImage;
}

int AverageGray( IplImage* pSrcImg )
{
    int gray = 0;	
    IplImage *pImg = cvCreateImage(cvSize(pSrcImg->width, pSrcImg->height),  IPL_DEPTH_8U,1);
    cvCvtColor(pSrcImg, pImg, CV_BGR2GRAY);

    uchar *imageData = (uchar*)pImg->imageData;

    for (int i=0; i<pImg->height; i+=20)
        for (int j=0; j<pImg->width; j+=20)
        {
            int iOffset = i*pImg->widthStep + j;		

            if (imageData[iOffset])
            {
                gray += imageData[iOffset];
            }				
        }

        int averagegray = gray/((pImg->height/20)*(pImg->width/20));
        cvReleaseImage(&pImg);

        return averagegray;
}

void DrawRect(IplImage *img, list<CvRect> &rects)
{
    list<CvRect>::iterator rect;
    for (rect = rects.begin(); rect!= rects.end(); rect++)
    {
        int x = rect->x;
        int y = rect->y;
        int w = rect->width;
        int h = rect->height;

        //画框
        cvRectangle(img, cvPoint(x, y), cvPoint(x+w, y+h), 
            CV_RGB(0, 0, 255), 3, CV_AA, 0);

    }
}

void DrawRect(IplImage *img, CvSeq *objRects)
{
    for (int i=0; i<(objRects->total?objRects->total:0); i++)
    {
        CvRect* rect = (CvRect*) cvGetSeqElem(objRects, i);
        int x = rect->x;
        int y = rect->y;
        int w = rect->width;
        int h = rect->height;

        cvRectangle(img, cvPoint(x, y), cvPoint(x + w, y + h),
            CV_RGB(0, 0, 255), 3, CV_AA, 0);
    }
}


void SaveAlarmImage(IplImage *pImg)
{
    if (pImg == NULL)
    {
        return;
    }

    static int filenamecount = 0;
    char filename[256];

    sprintf(filename, "./JPEG/Alarm/%d_%ld_alarm.jpeg", filenamecount, time(0));
    cvSaveImage(filename, pImg);
    filenamecount++;

    printf("write image ok!!!!\n");
}



DWORD WINAPI CaptureThread(void* lpParameter)
{

    CCamera *pCamera = (CCamera *) lpParameter;

    if (!pCamera->IsInitialized())
    {
        pCamera->Initialize();
    }
    

    int pos = 0;
    time_t capTime = 0;

    IplImage *pIplCap = NULL;

    printf("CaptureThread[%d] ok.......\n", pCamera->GetCameraId());

    g_nCaptureThreadOK[pCamera->GetCameraId()] = 1;
    int count = 0;

    //--------------capture frame debug-------------
    int frameCount = 0;
    time_t frametime = time(0);
    //-----------------------------------------------


    while (!g_bQuit)
    {
        pIplCap = pCamera->CaptureFrame();
        if (pIplCap == NULL)
        {
            printf("capture frame fail\n");
            exit(0);
        }
        else
        {
            printf("captured frame in %s\n", (LPCTSTR) pCamera->GetName());
        }


        //--------------capture frame debug-------------
        frameCount++;
        if (time(0) - frametime > 1)
        {
            printf("capture frame %d frame/s\n", frameCount);
            frametime = time(0);
            frameCount = 0;
        }
        //------------------------------------------------

        capTime = time(0);

//         if (AverageGray(pIplCap) < 20)
//         {
//             BYTE ATMno = 0x00;
//             BYTE statusErr = 0x00;
//             //g_pSystemStatus->SetVideoStatus(ATMno, (BYTE)nCardNo, statusErr);
//         }
//         else
        {
            //将视频加入到缓冲区等待处
            //if (count >=4)
            {
                ProImage *pProImg = new ProImage;
                pProImg->time = time(0);
                pProImg->pIplImage = NULL;
                pProImg->pIplImage = cvCloneImage(pIplCap);
                g_ProcessBuf[pCamera->GetCameraId()].AddTail(pProImg);
                count = 0;
            }
        }

        ImageInfo imgInfo;
        imgInfo.time = capTime;//时间
        imgInfo.cardno = pCamera->GetCameraId();//

        //发送实时视??

        if (pResultSock->GetValidLineCount() > 0)
        {
            CJSByteArray byteArr(128 * 1024);

            int nPackLen = PackImage(byteArr, imgInfo, *pIplCap);

            if (nPackLen > 0)
            {
                int len = pResultSock->SendData(pResultSock->GetLineByIndex(0), \
                        byteArr.GetData(), byteArr.Length());
            }
            else
            {
            }
        }
        else
        {
        }

        count++;
        Sleep(500);

    }
    
    printf("CaptureThread\n");

    return 0;
}

inline time_t Now() 
{
    return time(NULL);
}

void SetPersonDetected(bool detected) 
{
    bPerson = detected;
}

void SendCoveredAlarm( ProImage * pBuffer ) 
{
    CameraBlockPack cameraBlock;
    cameraBlock.time = pBuffer->time;
    cameraBlock.pImg = pBuffer->pIplImage;

    CJSByteArray byteArr;
    int len = PackCameraBlockPack(byteArr, cameraBlock);
    if (len > 0)
    {
        if (pResultSock->GetValidLineCount() > 0)
        {
            int len = pResultSock->SendData(
                pResultSock->GetLineByIndex(0), byteArr.GetData(),
                byteArr.Length());
            printf("Send camera blocked OK......%d\n", len);
        }
    }
}

DWORD WINAPI HProcessThread(void* lpParameter)
{
    
    IplImage *lplImage;
    CRectMatch RM(176, 144);

    time_t personLastSeenTime = Now();

    CFaceSelect faceSearcher;
    CCameraCoverDetector coveredDetector;
    
    while (1)
    {
        ProImage *pBuffer = (ProImage*) g_ProcessBuf[0].RemoveHead();
        if (pBuffer)
        {
            //检测覆盖报警
            auto isCovered = coveredDetector.IsCovered(pBuffer->pIplImage);
            if (isCovered)
            {
                SendCoveredAlarm(pBuffer);

                cvReleaseImage(&pBuffer->pIplImage);
                pBuffer->pIplImage = NULL;
                delete pBuffer;
                pBuffer = NULL;
                continue;
            }


            lplImage = cvCloneImage(pBuffer->pIplImage);

            Frame frame;
            ZeroMemory(&frame, sizeof(frame));
            frame.image = lplImage;
            faceSearcher.AddInFrame(frame);

            Target* pTarget = NULL;
            int faceCount = faceSearcher.SearchFaces(&pTarget);

            if (faceCount > 0)
            {
                SetPersonDetected(true);
                personLastSeenTime = pBuffer->time;
            }
            else
            {
                if (pBuffer->time - personLastSeenTime > g_pConfigure->GetNoPersonTime())
                {
                    SetPersonDetected(false);
                }
            }

            list<CvRect> match;
            for (int i = 0; i < faceCount; i++)
            {
                CvRect r = pTarget->FaceRects[i];
                match.push_back(r);
            }

            list<SOptImg> alarmImg;
            RM.match(match, lplImage, pBuffer->time, alarmImg);

            for (list<SOptImg>::iterator it = alarmImg.begin(); it != alarmImg.end(); ++it)
            {
                               
                IplImage *lplImageFace = GetSubImage(it->img, it->optRect);
                //send face to display
                ATM_FaceInfo faceinfo;
                faceinfo.time = it->time;
                strcpy(faceinfo.ICcardno, "3480218349234234");

                CJSByteArray byteArr;
                IplImage *iplimg = cvCloneImage(it->img);
                IplImage *iplface = cvCloneImage(lplImageFace);
                
                PackTradeInfo(byteArr, faceinfo, *iplimg, *iplface);

                printf("Yes get FACE ALARM.....\n");
                if (pResultSock->GetValidLineCount() > 0)
                {
                    int len = pResultSock->SendData(
                            pResultSock->GetLineByIndex(0), byteArr.GetData(),
                            byteArr.Length());
                    printf("Send Faceinfo OK......%d\n", len);
                }

                cvReleaseImage(&(it->img));
            }

            cvReleaseImage(&lplImage);
            cvReleaseImage(&pBuffer->pIplImage);
            pBuffer->pIplImage = NULL;
            delete pBuffer;
            pBuffer = NULL;
        }
        Sleep(20);
    }
    return 0;
}

DWORD WINAPI VProcessThread(void* lpParameter)
{
    CBkg* pBkg = new CBkg();
    CObjStableDetect *pStableDetect = new CObjStableDetect;

    static int nFrameCount = 0;
    char background[] = "background";
    char detect[] = "detect";

    
    while (1)
    {

        ProImage *pBuffer = (ProImage*) g_ProcessBuf[1].RemoveHead();
        //printf("VProcessThread OK.............pBuffer=%d\n", pBuffer);
        if (pBuffer)
        {
            IplImage * lplImage;
            lplImage = pBuffer->pIplImage;
            if (lplImage == NULL)
            {
                printf("frame is NULL!!!!!\n");
                Sleep(10);
                continue;
            }

            if (nFrameCount < 10)
            {
                nFrameCount++;
                Sleep(10);
                continue;
            }

            pBkg->SetUpBkgMode(true);

             //是否改新背景
            if (g_pAlarm->GetAlarmAckInfo() == 0x01)
            {
                printf("update background!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
                pBkg->SetBkg(lplImage);
                g_pAlarm->ResetAlarmAckInfo();

                //SetBkg与FrameProcess不能同时调用
                Sleep(1000);
                continue;
            }

            pBkg->FrameProcess(lplImage);

            CvSeq *objRects = 0;
            pBkg->GetObjRects(objRects);
            if (objRects != NULL)
            {
                if (objRects->total <= 0)
                {
                     g_pAlarm->NoAlarm();
                }

                
               
                //----------------------------------------------end1

                pStableDetect->StableDetect(objRects);

                list<CvRect> stableObjRects;
                pStableDetect->GetStableObjRects(stableObjRects);

                if (objRects->total > 0)
                {
                    printf("objRects->total=%d, stableObjRects.size()=%d, bPerson=%d\n",\
                            objRects->total, stableObjRects.size(), bPerson);
                }

                if (stableObjRects.size() > 0)
                {
                    if (!bPerson)
                    {
                        ATM_AlarmInfo alarmInfo;
                        DrawRect(lplImage, stableObjRects);
                        alarmInfo.time = pBuffer->time;
                        alarmInfo.cardno = 0x01;
                        alarmInfo.rectNum = stableObjRects.size();
                        
                        int slen = g_pAlarm->SendAlarm(alarmInfo, stableObjRects, lplImage);

                        //SaveAlarmImage(lplImage);
                    }
                }
            }
            else
            {
                printf("GetObjRects=NULL\n");
//                 g_pAlarm->NoAlarm();
            }

            cvReleaseImage(&pBuffer->pIplImage);
            pBuffer->pIplImage = NULL;
            delete pBuffer;
            pBuffer = NULL;
        }
        //printf("VProcessThread end\n");
        Sleep(10);
    }

    delete pBkg;
    delete pStableDetect;
    return 0;

}


void OnResultSocketRecv(CSock* pSocket, int n)
{
    static char buffer[512 * 1024];
    int len = pSocket->Recv((char*) buffer, 512 * 1024);
    while (len != 0)
    {
        printf("onResultSock-------------------------------type:%d\n", \
            ntohl(*(int*) buffer));
        switch (ntohl(*(int*) buffer))
        {
        case MSG_ALARMACK:
            g_pAlarm->AlarmAckHandle(buffer);
            break;
        case MSG_QUERYSYSTEMSTATUS:
            //g_pSystemStatus->SystemStatusAck();    //现在只管了一个
            break;
        case MSG_SYNCSYSTEMTIME:  //校时
            //g_pConfigure->SyncSystemTime(buffer);
            break;
        case MSG_CONFIG_NOPERSONTIME: //设置确认没人时间
            g_pConfigure->SetNoPersonTime(buffer);
            break;
        default:
            break;
        }

        len = pSocket->Recv((char*) buffer, 512 * 1024);

    }
}


void InitializeNetwork()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD( 2, 2 );

    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 ) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if ( LOBYTE( wsaData.wVersion ) != 2 ||
        HIBYTE( wsaData.wVersion ) != 2 ) {
            /* Tell the user that we could not find a usable */
            /* WinSock DLL.                                  */
            WSACleanup( );
    }
}

void callback(IplImage* image)
{
    cvcamPause();

    cvcamGetProperty(0,"raw_image",&image);

    cvSaveImage("dfd.jpg", image);
    
    cvcamResume();
}

void stereocallback(IplImage* image1, IplImage* image2) 
{
    TRACE("%d, %d\n", image1, image2);

    static bool initialized = false;

    if (!initialized)
    {
        cvNamedWindow("1");
        cvNamedWindow("2");
        initialized = true;
    }

    ProImage *pProImg = new ProImage;
    pProImg->time = time(0);
    pProImg->pIplImage = cvCloneImage(image1);
    g_ProcessBuf[0].AddTail(pProImg);
    
    ProImage *p2 = new ProImage;
    p2->pIplImage = cvCloneImage(image2);
    pProImg->time = Now();
    g_ProcessBuf[1].AddTail(p2);
    
    //Process 2 images here
}

int CvCam() 
{
    auto camCount = cvcamGetCamerasCount( );

    int cam1 = 0; 
    int cam2 = 1;

    cvcamSetProperty(cam1, CVCAM_PROP_ENABLE, CVCAMTRUE); 
    cvcamSetProperty(cam1, CVCAM_PROP_RENDER, CVCAMTRUE);  //We'll render stream from this source
    cvNamedWindow("Cam1", 1); 
    cvcamWindow MyWin1 = (cvcamWindow)cvGetWindowHandle("Cam1");
    cvcamSetProperty(cam1, CVCAM_PROP_WINDOW, &MyWin1);   // Selects a window for  video rendering 

    //Same code for camera 2
    cvcamSetProperty(cam2, CVCAM_PROP_ENABLE, CVCAMTRUE); 
    cvcamSetProperty(cam2, CVCAM_PROP_RENDER, CVCAMTRUE); 
    cvNamedWindow("Cam2", 1); 
    cvcamWindow MyWin2 = (cvcamWindow)cvGetWindowHandle("Cam2");
    cvcamSetProperty(cam2, CVCAM_PROP_WINDOW, &MyWin2);   

    //If you want to open the property dialog for setting the video format parameters, uncomment this line
    //cvcamGetProperty(cam1, CVCAM_VIDEOFORMAT, NULL);
    //cvcamGetProperty(cam2, CVCAM_VIDEOFORMAT, NULL);

    //cvcamSetProperty(0, CVCAM_PROP_CALLBACK, callback);

    cvcamSetProperty(cam1, CVCAM_STEREO_CALLBACK , stereocallback); //stereocallback is the function running to process every frames

    cvcamInit(); 
    cvcamStart();

    //Your app is working 
    while (1) 
    { 
        int key = cvWaitKey(5); 
        if (key == 27) break; 
    }

    cvcamStop( ); 
    cvcamExit( );


    return 1;
}
int MainFunction(int argc, TCHAR* argv[], TCHAR* envp[])
{
	while(true)
	{
		auto cameraCount = CWebCam::GetCameraCount();
		if (cameraCount >= 2) break;
    
		cout<<"---------------"<<endl;
		cout<<"仅检测到"<<cameraCount<<"个摄像头"<<endl;
		cout<<"请检查摄像头连接"<<endl;
		Sleep(5000);
	}

    
    InitializeNetwork();

    bool bQuit = false;
 
    g_pConfigure = new CConfigure;

    //初始化网络模
    pResultSock = new CSocket(10000, true);
    pResultSock->SetCallBack(OnResultSocketRecv);
    bool b1 = pResultSock->Listen(3);
    if (b1)
        printf("Socket Module init.....OK\n");

    //初始化报警模块
    g_pAlarm = new CAlarm;
    g_pAlarm->SetSocket(pResultSock);

    //初始化系统状态类
    //g_pSystemStatus = new CSystemStatus;
    //g_pSystemStatus->SetSocket(pResultSock);

    //启动抓拍线程
    BYTE ATMno = 0x00;
    BYTE cardNo;
    BYTE statusErr = 0x00;
    BYTE statusOK = 0x01;
    DWORD CapTh[2];
    int nCardNo[2];

    char horizontalCamName[] = "水平摄像头";
    char verticalCamName[] = "垂直摄像头";

//     auto videohCam = new CVideoFileCam("M:\\测试图片\\视频\\h.avi");
//     videohCam->SetCameraId(0);
//     videohCam->SetName(horizontalCamName);
//     videohCam->Initialize();


//     auto directoryHCam = new CDirectoryCam("M:\\测试图片\\视频\\少量");
//     directoryHCam->SetCameraId(0);
//     directoryHCam->SetName("水平摄像头");

    auto webCamH = new CWebCam();
    webCamH->SetCameraId(0);
    webCamH->deviceId = 0;
    webCamH->SetName(horizontalCamName);
    webCamH->Initialize();

    auto webCamV = new CWebCam();
    webCamV->SetCameraId(1);
    webCamV->deviceId = 1;
    webCamV->SetName(verticalCamName);
    webCamV->Initialize();

    
//     auto verticalCam = new CVideoFileCam("M:\\测试图片\\视频\\v.avi");
//     verticalCam->SetCameraId(1);
//     verticalCam->SetName("垂直摄像头");
//     verticalCam->Initialize();



    DWORD threaId;
    CreateThread(NULL, 0, CaptureThread, webCamH, 0, &threaId);
    CreateThread(NULL, 0, CaptureThread, webCamV, 0, &threaId);


    //启动水平人脸检测线
    DWORD HFaceTh;
    int stackSize = 2*1024*1024;
    CreateThread(NULL, stackSize, HProcessThread, NULL, 0, &HFaceTh);

    //启动垂直异常物检测线
    DWORD VObjTh;
    CreateThread(NULL, stackSize, VProcessThread, 0, 0, &VObjTh);

    while(true)
    {
        Sleep(1000);
    }

    delete g_pAlarm;
    //delete g_pSystemStatus; 
    delete g_pConfigure;
    delete pResultSock;


    return 0;
}
